/**
 * @Description 上传组件的钩子函数
 * @Author 莫发达
 * @create 2024-09-05 09:58 周四
 */

import type {Ref} from "vue";
import {ElMessage} from "element-plus";
import {formatFileSize, getFileExtension} from "@/utils/file-util.ts";
import {type UploadFile, type UploadInstance, type UploadRawFile} from "element-plus";
import type {IFileInfo, IUploadFile, IUploadProps} from "@/components/base-upload/types.ts";
import {isEffectiveURL, refreshURL} from "./util.ts";
import {openPreview} from "@shared/base/open";
import {isPlainObject} from "sf-utils2";

/**
 * 自定义上传钩子函数
 * @param props 组件的属性
 * @param uploadRef 上传组件的引用
 * @returns 包含上传钩子函数的对象
 */
export function useUploadHooks(props: IUploadProps, uploadRef: Ref<UploadInstance>) {
  /**
   * 上传文件之前的钩子，参数为上传的文件
   * @param file 上传的文件
   * @returns {boolean | Promise<boolean | void>} 返回是否允许上传
   */
  const onBeforeUpload = (file: File): boolean | void | Promise<boolean | void> => {
    const {fileSize, accept, beforeUpload} = props;

    // 获取文件扩展名并转换为大写
    const fileExt = getFileExtension(file.name).toUpperCase();
    // 将 accept 属性中的文件类型转换为大写并去除空格
    const supportExts = accept?.split(',').map(item => item.toUpperCase().trim()) ?? [];

    // 检查文件类型是否在支持的扩展名列表中
    if (supportExts.length && !supportExts.includes(`.${fileExt}`)) {
      ElMessage.error({
        message: `仅支持扩展名为 ${accept} 的文件`,
        duration: 4000,
      });
      return false;
    }

    // 检查文件大小是否超过限制
    if (fileSize && file.size > 1024 * 1024 * fileSize) {
      const sizeText = formatFileSize(fileSize * 1024 * 1024, 0);
      ElMessage.error({
        message: `请上传小于 ${sizeText} 的文件`,
        duration: 4000,
      });
      return false;
    }

    // 调用 beforeUpload 钩子函数，如果存在的话
    return beforeUpload?.(file) ?? true;
  };

  /**
   * 文件上传成功后的回调函数
   * @param response 服务器响应
   * @param file 上传的文件
   * @param fileList 文件列表
   */
  const onFileSuccess = async (response: {
    code: number,
    data: IFileInfo,
    message: string
  }, file: IUploadFile, fileList: IUploadFile[]) => {
    await props.beforeSuccess?.(response, file, fileList);

    // 设置文件大小, 仅在上传成功时设置
    if (response?.data && isPlainObject(response?.data)) response.data.fileSize = file.size

    if (response.code !== 200) {
      file.status = 'fail';
      ElMessage.error({
        message: response.message,
        duration: 4000
      })
    } else {
      file.status = 'success';
      file.file = response.data
      // 删除 raw 属性，避免上传文件时的内存泄漏
      delete file.raw;
    }

    // 调用 onSuccess 回调函数，如果存在的话
    props.onSuccess?.(response, file, fileList);
  };

  /**
   * 文件数量超出限制时的回调函数
   * @param selectedFiles 选中的文件
   * @param uploadedFiles 已上传的文件
   */
  const onExceed = (selectedFiles: File[], uploadedFiles: IUploadFile[]) => {
    const {limit, exceedReplace, autoUpload} = props;

    // 如果需要替换，并且选中的文件数量 大于 限制数量减去已上传的文件数量
    if (exceedReplace && selectedFiles.length > limit - uploadedFiles.length) {
      const needReplaceCount = selectedFiles.length - (limit - uploadedFiles.length);

      // 截取选中的文件，保证不超过限制
      if (selectedFiles.length > limit) {
        // 选中的文件数量大于限制数量，截取后 limit 个文件
        selectedFiles = selectedFiles.slice(-limit);
      }

      // 移除掉多余的文件
      uploadedFiles.splice(-needReplaceCount);
    }

    // 一定要处理完之后在计算剩余的上传文件数量，否则会出现计算错误
    const remainingUploadSlots = limit - uploadedFiles.length;

    // 处理剩余的上传文件
    if (remainingUploadSlots > 0) {
      selectedFiles.slice(0, remainingUploadSlots).forEach(file => {
        uploadRef.value.handleStart(file as UploadRawFile);
      });

      // 如果设置了自动上传，那么直接提交
      if (autoUpload) uploadRef.value.submit();
    }

    // 超出限制的文件数量
    const exceedCount = selectedFiles.length - remainingUploadSlots;
    if (limit && exceedCount > 0) {
      ElMessage.warning({
        message: `当前限制选择 ${limit} 个文件，超出的 ${exceedCount} 个文件将会丢弃。`,
        duration: 4000
      });
    }

    // 调用 onExceed 回调函数，如果存在的话
    props.onExceed?.(selectedFiles, uploadedFiles);
  };

  /**
   * 重试上传文件
   * @param file
   */
  function onRetry(file: UploadFile) {
    file.percentage = 0
    file.status = 'ready'
    // 重新上传, 稍微延迟一下，不然会闪屏
    setTimeout(() => {
      uploadRef.value?.submit()
    }, 300)
  }

  /**
   * 文件预览
   * @param file
   */
  async function onPreview(file: IUploadFile) {
    const previewUrl = file.file?.previewUrl;
    if (!previewUrl) return;

    // 检查文件是否为有效 URL
    const isURLValid = isEffectiveURL(file.file);

    // 如果 URL 无效，刷新 URL 并获取新的文件路径
    if (!isURLValid) {
      file.file = await refreshURL(file.file);
      file.url = file.file?.previewUrl;
    }

    // 打开或下载文件
    openPreview([file.file?.previewUrl]);
  }

  /**
   * 删除文件
   * @param file
   */
  function onDelete(file: IUploadFile) {
    uploadRef.value.handleRemove(file as UploadFile);
  }

  return {
    onBeforeUpload,
    onFileSuccess,
    onExceed,
    onRetry,
    onPreview,
    onDelete
  };
}
